﻿/* Copyright (C) 2008 - 2011 by John Cronin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace tysila
{
    partial class Assembler
    {
        private MemberRequestor _requestor; public MemberRequestor Requestor { get { return _requestor; } }
        public class MemberRequestor
        {
            private Assembler ass; public Assembler Assembler { get { return ass; } }
            public MemberRequestor(Assembler assembler) { ass = assembler; }
            public List<Metadata> ModulesToAssemble = new List<Metadata>();
            internal object meth_lock = new object();
            internal object type_lock = new object();
            internal object ti_lock = new object();
            internal object fi_lock = new object();
            internal object mi_lock = new object();
            internal bool skip_checks = false;

            public void RequestMethod(Assembler.MethodToCompile mtc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(mtc.meth.m)))
                {
                    lock (meth_lock)
                    {
                        if (mtc.type.IsDelegate(ass) && (mtc.meth.Name == ".ctor"))
                            ass.RewriteDelegateCtor(mtc.msig.Method);
                        if ((skip_checks) || ((!ExistsIn(mtc, _requested_meths)) && (!ExistsIn(mtc, _compiled_meths))))
                            _requested_meths.Add(mtc, 0);
                    }
                }
            }

            public void RequestTypeInfo(Assembler.TypeToCompile ttc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ttc.type.m)))
                {
                    lock (ti_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ttc, _requested_tis)) && (!ExistsIn(ttc, _compiled_tis))))
                            _requested_tis.Add(ttc, 0);
                    }
                }
            }

            public void RequestType(Assembler.TypeToCompile ttc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ttc.type.m)))
                {
                    lock (type_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ttc, _requested_types)) && (!ExistsIn(ttc, _compiled_types))))
                            _requested_types.Add(ttc, 0);
                    }
                }
            }

            public void RequestMethodInfo(Assembler.MethodToCompile mtc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(mtc.meth.m)))
                {
                    lock (mi_lock)
                    {
                        if (mtc.type.IsDelegate(ass) && (mtc.meth.Name == ".ctor"))
                            ass.RewriteDelegateCtor(mtc.msig.Method);
                        if ((skip_checks) || ((!ExistsIn(mtc, _requested_mis)) && (!ExistsIn(mtc, _compiled_mis))))
                            _requested_mis.Add(mtc, 0);
                    }
                }
            }

            public void RequestFieldInfo(Assembler.FieldToCompile ftc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ftc.field.m)))
                {
                    lock (fi_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ftc, _requested_fis)) && (!ExistsIn(ftc, _compiled_fis))))
                            _requested_fis.Add(ftc, 0);
                    }
                }
            }

            public void ExcludeMethod(Assembler.MethodToCompile mtc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(mtc.meth.m)))
                {
                    lock (meth_lock)
                    {
                        if (mtc.type.IsDelegate(ass) && (mtc.meth.Name == ".ctor"))
                            ass.RewriteDelegateCtor(mtc.msig.Method);
                        if ((skip_checks) || ((!ExistsIn(mtc, _requested_meths)) && (!ExistsIn(mtc, _compiled_meths))))
                            _compiled_meths.Add(mtc, 0);
                    }
                }
            }

            public void ExcludeTypeInfo(Assembler.TypeToCompile ttc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ttc.type.m)))
                {
                    lock (ti_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ttc, _requested_tis)) && (!ExistsIn(ttc, _compiled_tis))))
                            _compiled_tis.Add(ttc, 0);
                    }
                }
            }

            public void ExcludeType(Assembler.TypeToCompile ttc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ttc.type.m)))
                {
                    lock (type_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ttc, _requested_types)) && (!ExistsIn(ttc, _compiled_types))))
                            _compiled_types.Add(ttc, 0);
                    }
                }
            }

            public void ExcludeMethodInfo(Assembler.MethodToCompile mtc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(mtc.meth.m)))
                {
                    lock (mi_lock)
                    {
                        if (mtc.type.IsDelegate(ass) && (mtc.meth.Name == ".ctor"))
                            ass.RewriteDelegateCtor(mtc.msig.Method);
                        if ((skip_checks) || ((!ExistsIn(mtc, _requested_mis)) && (!ExistsIn(mtc, _compiled_mis))))
                            _compiled_mis.Add(mtc, 0);
                    }
                }
            }

            public void ExcludeFieldInfo(Assembler.FieldToCompile ftc)
            {
                if ((ModulesToAssemble.Count == 0) || (ModulesToAssemble.Contains(ftc.field.m)))
                {
                    lock (fi_lock)
                    {
                        if ((skip_checks) || ((!ExistsIn(ftc, _requested_fis)) && (!ExistsIn(ftc, _compiled_fis))))
                            _compiled_fis.Add(ftc, 0);
                    }
                }
            }
            private bool ExistsIn(Assembler.MethodToCompile mtc, Dictionary<Assembler.MethodToCompile, int> mtcs)
            {
                return mtcs.ContainsKey(mtc);
            }

            private bool ExistsIn(Assembler.TypeToCompile ttc, Dictionary<Assembler.TypeToCompile, int> ttcs)
            {
                return ttcs.ContainsKey(ttc);
            }

            private bool ExistsIn(Assembler.FieldToCompile ftc, Dictionary<Assembler.FieldToCompile, int> ftcs)
            {
                return ftcs.ContainsKey(ftc);
            }

            internal Dictionary<Assembler.TypeToCompile, int> _requested_tis = new Dictionary<Assembler.TypeToCompile, int>();
            internal Dictionary<Assembler.TypeToCompile, int> _requested_types = new Dictionary<Assembler.TypeToCompile, int>();
            internal Dictionary<Assembler.MethodToCompile, int> _requested_meths = new Dictionary<Assembler.MethodToCompile, int>();
            internal Dictionary<Assembler.FieldToCompile, int> _requested_fis = new Dictionary<Assembler.FieldToCompile, int>();
            internal Dictionary<Assembler.MethodToCompile, int> _requested_mis = new Dictionary<Assembler.MethodToCompile, int>();

            internal Dictionary<Assembler.TypeToCompile, int> _compiled_tis = new Dictionary<Assembler.TypeToCompile, int>();
            internal Dictionary<Assembler.TypeToCompile, int> _compiled_types = new Dictionary<Assembler.TypeToCompile, int>();
            internal Dictionary<Assembler.MethodToCompile, int> _compiled_meths = new Dictionary<Assembler.MethodToCompile, int>();
            internal Dictionary<Assembler.FieldToCompile, int> _compiled_fis = new Dictionary<Assembler.FieldToCompile, int>();
            internal Dictionary<Assembler.MethodToCompile, int> _compiled_mis = new Dictionary<Assembler.MethodToCompile, int>();

            public bool MoreTypeInfos { get { if (_requested_tis.Count == 0) return false; return true; } }
            public bool MoreTypes { get { if (_requested_types.Count == 0) return false; return true; } }
            public bool MoreMethods { get { if (_requested_meths.Count == 0) return false; return true; } }
            public bool MoreFieldInfos { get { if (_requested_fis.Count == 0) return false; return true; } }
            public bool MoreMethodInfos { get { if (_requested_mis.Count == 0) return false; return true; } }
            public bool MoreToDo { get { lock (ti_lock) { lock (meth_lock) { lock (type_lock) { lock (fi_lock) { lock (mi_lock) { if (MoreTypeInfos || MoreTypes || MoreMethods || MoreFieldInfos || MoreMethodInfos) return true; else return false; } } } } } } }

            public void PurgeRequestedTypes() { _requested_types.Clear(); }
            public void PurgeRequestedTypeInfos() { _requested_tis.Clear(); }
            public void PurgeRequestedMethods() { _requested_meths.Clear(); }
            public void PurgeRequestedFieldInfos() { _requested_fis.Clear(); }
            public void PurgeRequestedMethodInfos() { _requested_mis.Clear(); }
            public void PurgeAssembledTypes() { _compiled_types.Clear(); }
            public void PurgeAssembledTypeInfos() { _compiled_tis.Clear(); }
            public void PurgeAssembledMethods() { _compiled_meths.Clear(); }
            public void PurgeAssembledFieldInfos() { _compiled_fis.Clear(); }
            public void PurgeAssembledMethodInfos() { _compiled_mis.Clear(); }
            public void PurgeAll() { PurgeAssembledMethods(); PurgeAssembledTypeInfos(); PurgeAssembledTypes(); PurgeRequestedMethods(); PurgeRequestedTypeInfos(); PurgeRequestedTypes();
            PurgeAssembledFieldInfos(); PurgeAssembledMethodInfos(); PurgeRequestedFieldInfos(); PurgeRequestedMethodInfos();
            }

            public Assembler.MethodToCompile GetNextMethod()
            {
                if (!MoreMethods)
                    throw new Exception("No more methods");
                
                IEnumerator<MethodToCompile> e = _requested_meths.Keys.GetEnumerator();
                e.MoveNext();
                Assembler.MethodToCompile ret = e.Current;
                _compiled_meths.Add(ret, 0);
                _requested_meths.Remove(ret);
                return ret;
            }
            public Assembler.TypeToCompile GetNextType()
            {
                if (!MoreTypes)
                    throw new Exception("No more types");

                IEnumerator<TypeToCompile> e = _requested_types.Keys.GetEnumerator();
                e.MoveNext();
                Assembler.TypeToCompile ret = e.Current;
                _compiled_types.Add(ret, 0);
                _requested_types.Remove(ret);
                return ret;
            }
            public Assembler.TypeToCompile GetNextTypeInfo()
            {
                if (!MoreTypeInfos)
                    throw new Exception("No more typeinfos");

                IEnumerator<TypeToCompile> e = _requested_tis.Keys.GetEnumerator();
                e.MoveNext();
                Assembler.TypeToCompile ret = e.Current;
                _compiled_tis.Add(ret, 0);
                _requested_tis.Remove(ret);
                return ret;
            }
            public Assembler.MethodToCompile GetNextMethodInfo()
            {
                if (!MoreMethodInfos)
                    throw new Exception("No more methodinfos");

                IEnumerator<MethodToCompile> e = _requested_mis.Keys.GetEnumerator();
                e.MoveNext();
                Assembler.MethodToCompile ret = e.Current;
                _compiled_mis.Add(ret, 0);
                _requested_mis.Remove(ret);
                return ret;
            }
            public Assembler.FieldToCompile GetNextFieldInfo()
            {
                if (!MoreFieldInfos)
                    throw new Exception("No more fieldinfos");
                IEnumerator<FieldToCompile> e = _requested_fis.Keys.GetEnumerator();
                e.MoveNext();
                Assembler.FieldToCompile ret = e.Current;
                _compiled_fis.Add(ret, 0);
                _requested_fis.Remove(ret);
                return ret;
            }
        }
    }
}
